//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	SerializationExts.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Smart
{
	[DebuggerStepThrough]
	public static class SerializationExts
	{
		public static Encoding JsonEncoding = Encoding.UTF8;

		private const string JsonSerializerCachePrefix = @"SHJSS_";
		private static readonly TimeSpan JsonSerializerCacheTimeout = new TimeSpan(1, 0, 0, 0);

		public static DataContractJsonSerializer GetJsonSerializer(Type type)
		{
			var cacheKey = String.Concat(JsonSerializerCachePrefix, type.GetHashCodeX());

			return
				SCache.Get(cacheKey, JsonSerializerCacheTimeout, key => new DataContractJsonSerializer(type));
		}

		public static string ToJson<T>(this T value)
			where T : class
		{
			if (value == null)
				return String.Empty;

			//Create a stream to serialize the object to.
			var ms = new MemoryStream();

			// Serializer the object to the stream
			var serializer = GetJsonSerializer(typeof(T));

			var serialized = false;

			try
			{
				serializer.WriteObject(ms, value);
				serialized = true;
			}
			catch (Exception x)
			{
				H.LX("SerializationExts.ToJson", x);
			}

			if (!serialized)
				return String.Empty;

			var json = ms.ToArray();

			ms.Close();

			return JsonEncoding.GetString(json);
		}

		public static T FromJson<T>(this string json)
			where T : class
		{
			if (String.IsNullOrEmpty(json))
				return default(T);

			var res = default(T);

			using (var ms = new MemoryStream(JsonEncoding.GetBytes(json)))
			{
				var serializer = GetJsonSerializer(typeof(T));

				try
				{
					res = (T)serializer.ReadObject(ms);
				}
				catch (Exception x)
				{
					H.LX("SerializationExts.FromJson", x);
				}

				ms.Close();
			}

			return res;
		}

		private const string XmlSerializerCachePrefix = @"SHXS_";
		private static readonly TimeSpan XmlSerializerCacheTimeout = new TimeSpan(1, 0, 0, 0);

		public static XmlSerializer GetXmlSerializer(Type type)
		{
			var cacheKey = String.Concat(XmlSerializerCachePrefix, type.GetHashCodeX());

			return
				SCache.Get(cacheKey, XmlSerializerCacheTimeout, key => new XmlSerializer(type));
		}

		public static T DeserializeAndLoadXml<T>(this string filePath)
		{
			using (var reader = XmlReader.Create(filePath))
			{
				var serializer = GetXmlSerializer(typeof(T));

				var result = (T)serializer.Deserialize(reader);

				return result;
			}
		}

		public static T DeserializeXml<T>(this string xml)
		{
			using (var input = new StringReader(xml))
				return input.DeserializeXml<T>();
		}


		public static T DeserializeXml<T>(this Stream input)
		{
			using (var reader = XmlReader.Create(input))
			{
				var serializer = GetXmlSerializer(typeof(T));

				var result = (T)serializer.Deserialize(reader);

				return result;
			}
		}

		public static T DeserializeXml<T>(this TextReader input)
		{
			using (var reader = XmlReader.Create(input))
			{
				var serializer = GetXmlSerializer(typeof(T));

				var result = (T)serializer.Deserialize(reader);

				return result;
			}
		}

		public static string SerializeToXml(this object data)
		{
			var sb = new StringBuilder();

			using (var writer = XmlWriter.Create(sb))
			{
				var serializer = GetXmlSerializer(data.GetType());

				serializer.Serialize(writer, data);

				return sb.ToString();
			}
		}

		public static void PutInt(this StringBuilder dst, int value)
		{
			dst.Append(value).Append(',');
		}

		private static readonly char[] IntCharsWithSpace = new[] { ' ', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

		public static int GetInt(this string source, int iStartingPosition, out int iNextPosition, int defaultValue)
		{
			iNextPosition = source.Length;

			for (int I = iStartingPosition, ICnt = source.Length; I < ICnt; I++)
			{
				if (source[I].Out(IntCharsWithSpace))
				{
					iNextPosition = I;
					break;
				}
			}

			if (iStartingPosition == iNextPosition) return defaultValue;

			var iRes = source.Substring(iStartingPosition, iNextPosition - iStartingPosition).SToInt(defaultValue);

			if (iNextPosition >= 0 && iNextPosition < source.Length)
			{
				if (source[iNextPosition] == ',')
					iNextPosition++;
			}

			if (iNextPosition >= source.Length)
				iNextPosition = -1;

			return iRes;
		}

		public static void PutString(this StringBuilder dst, string value)
		{
			PutInt(dst, (value == null ? -1 : value.Length));

			if (!String.IsNullOrEmpty(value))
				dst.Append(value);
		}

		public static void PutStringBuilder(this StringBuilder dst, StringBuilder value)
		{
			PutInt(dst, (value == null ? -1 : value.Length));

			if (value != null && value.Length > 0)
				dst.Append(value);
		}

		public static string GetString(this string source, int iSP, out int iNP)
		{
			var iSl = GetInt(source, iSP, out iNP, -1);

			if (iSl < 0) return null;
			if (iSl == 0) return String.Empty;

			var res = source.Substring(iNP, iSl);

			iNP += iSl;

			if (iNP >= source.Length)
				iNP = -1;

			return res;
		}
	}
}